EKSで「CloudWatch Container Insights」を利用できるように設定する
みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。
CloudWatchの新機能として、ECSやEKSなどのコンテナワークロードのパフォーマンスとログデータを収集して分析することができる Container Insights が2019年9月にリリースされました。
Container Insightsの概要とECSで利用する設定手順については、リリース時に執筆されたブログ記事で紹介されています。
ECSやEKSのメトリクスを一括取得するContainer Insightsが一般公開!既存ECSクラスタも追加設定可能に! | Developers.IO
今回は、EKSでContainer Insightsを利用できるように設定してみました。
構成
設定手順
AWSドキュメント では、EKSでのContainer Insightsの設定方法について、2通りの方法が案内されています。
- 「クイックスタートセットアップ」で設定する
- ステップ・バイ・ステップで確認しながら設定する。
「1」の方法は、必要な設定を全て含んだKubernetesマニフェストを使うことで、一撃で設定を行うことができます。
「2」の方法は、一つずつ手順を追って設定する必要がありますが、EKSでContainer Insightsを利用できるようにするためにどのようなリソースや設定が必要なのか、問題が起きた時にどこを調べればよいのか、といったことを理解することができます。
今回は「2」の方法で進めることにします。
使用したツール類のバージョン
$ eksctl version 0.15.0 $ aws --version aws-cli/1.18.26 Python/3.6.9 Linux/4.4.0-18362-Microsoft botocore/1.15.26 $ kubectl version --client --short Client Version: v1.17.0
EKSクラスターの準備
Container Insightsを設定するEKSクラスターを準備します。
eksctl
コマンドを使用してクラスターを作成します。
eksctl create cluster \ --name eks-example \ --nodegroup-name ng-example \ --node-type t3.large \ --nodes 2 \ --managed
これで、ワーカーノードを2つ持つEKSクラスターが作成されます。
前提条件: ワーカーノードのIAMロールに必要なポリシーを追加する
Container Insightsの設定を始める前に、前提条件を確認します。
今回の環境で必要となるのは「必要なポリシーをワーカーノードのIAMロールに追加するには」の部分です。
マネジメントコンソールでEKSクラスターの情報を表示して、ノードグループ名をクリックします。
ノードグループの詳細画面で「ノードIAMロール名」をクリックします。
IAMロールにポリシーをアタッチします。
AWS管理ポリシー CloudWatchAgentServerPolicy
を選択して、アタッチします。
これで、ワーカーノード (コンテナが動作するEC2インスタンス) にポリシーが設定されました。
このポリシーを設定する目的は、この後の手順で設定する「CloudWatchエージェント」「Fluentd」のDaemonSetにより実行されるコンテナに対して「CloudWatchへメトリクスやログを送信する権限」を与えることです。
ステップ1: メトリクス収集を行うCloudWatchエージェントを導入する
以下のページの内容に沿って進めます。
クラスターメトリクスを収集するよう CloudWatch エージェントをセットアップする - Amazon CloudWatch
なお、ここで言う「CloudWatchエージェント」とは、EC2インスタンスに対してインストールするCloudWatchエージェントではなく、Container Insightsのために用意されている専用のエージェントであり、コンテナイメージとして提供されています。
(1) 名前空間を作成する
Kubernetesには「名前空間」(Namespace) の概念があり、名前空間にはKubernetesのシステム関連リソースが配置されている「kube-system」や、ユーザーアプリケーションがデフォルトで配置される「default」などがあります。
ここでは、CloudWatchエージェント専用のNamespace「amazon-cloudwatch」を作成します。
Namespaceを作成するマニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cloudwatch-namespace.yaml
マニフェストファイルの内容 (クリックすると展開します)
# create amazon-cloudwatch namespace apiVersion: v1 kind: Namespace metadata: name: amazon-cloudwatch labels: name: amazon-cloudwatch
kubernetes apply
コマンドを使ってNamespaceリソースを作成します。
$ kubectl apply -f cloudwatch-namespace.yaml namespace/amazon-cloudwatch created
(2) サービスアカウントおよび関連リソースを作成する
「サービスアカウント」(ServiceAccount) とはKubernetes上のユーザーアカウントの一種であり、人が操作するためではなく、アプリケーション・サービスの実行主体となるアカウントです。
(AWSのIAMロールに近い概念です)
こちらもマニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cwagent/cwagent-serviceaccount.yaml
マニフェストファイルの内容 (クリックすると展開します)
# create cwagent service account and role binding apiVersion: v1 kind: ServiceAccount metadata: name: cloudwatch-agent namespace: amazon-cloudwatch --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: cloudwatch-agent-role rules: - apiGroups: [""] resources: ["pods", "nodes", "endpoints"] verbs: ["list", "watch"] - apiGroups: ["apps"] resources: ["replicasets"] verbs: ["list", "watch"] - apiGroups: ["batch"] resources: ["jobs"] verbs: ["list", "watch"] - apiGroups: [""] resources: ["nodes/proxy"] verbs: ["get"] - apiGroups: [""] resources: ["nodes/stats", "configmaps", "events"] verbs: ["create"] - apiGroups: [""] resources: ["configmaps"] resourceNames: ["cwagent-clusterleader"] verbs: ["get","update"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: cloudwatch-agent-role-binding subjects: - kind: ServiceAccount name: cloudwatch-agent namespace: amazon-cloudwatch roleRef: kind: ClusterRole name: cloudwatch-agent-role apiGroup: rbac.authorization.k8s.io
マニフェストでは3つのリソースを作成します。
「ServiceAccount」は、上で説明した通り、CloudWatchエージェントを実行する主体となるアカウントです。
「ClusterRole」は、アカウントに対して与える権限を定義するものです。
メトリクスを取得するために必要な「NodeやPodの情報を参照する権限」などが記述されています。
(Kubernetesの「ClusterRole」はAWSの「ロール」とは意味合いが異なり、どちらかと言うとAWSのIAMポリシーに近い概念です)
「ClusterRoleBinding」は、「ServiceAccount」に対して「ClusterRole」を割り当てるものです。
kubernetes apply
コマンドを使って各リソースを作成します。
$ kubectl apply -f cwagent-serviceaccount.yaml serviceaccount/cloudwatch-agent created clusterrole.rbac.authorization.k8s.io/cloudwatch-agent-role created clusterrolebinding.rbac.authorization.k8s.io/cloudwatch-agent-role-binding created
(3) ConfigMapを作成する
「ConfigMap」とは、Kubernetesのコンテナに対する設定情報などを格納するリソースです。
ConfigMapに格納された設定情報は、コンテナ内から環境変数やファイルなどを経由して参照することができます。
ここでは、CloudWatchエージェントのコンテナに対する設定をConfigMapに記述します。
ConfigMapを作成するマニフェストのテンプレートが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cwagent/cwagent-configmap.yaml
マニフェストテンプレートの内容は以下のようになっています。
# create configmap for cwagent config apiVersion: v1 data: # Configuration is in Json format. No matter what configure change you make, # please keep the Json blob valid. cwagentconfig.json: | { "logs": { "metrics_collected": { "kubernetes": { "cluster_name": "{{cluster_name}}", "metrics_collection_interval": 60 } }, "force_flush_interval": 5 } } kind: ConfigMap metadata: name: cwagentconfig namespace: amazon-cloudwatch
このマニフェストテンプレートを基に、環境に応じて内容を更新してマニフェストを準備します。
最低限の必要な修正は、11行目の "cluster_name": "{{cluster_name}}"
の部分です。
具体的には {{cluster_name}}
をEKSクラスター名で置き換えます。
今回の例であれば以下のようにします。
"cluster_name": "eks-example"
もしくは、EKSクラスター名の指定を省略することもできます。(その場合は11行目の全体を削除します)
その他の設定項目・設定の記述方法については、下記リンク先を参照してください。
CloudWatch エージェントの ConfigMap を作成する
ConfigMapの編集が終わりましたら、kubernetes apply
コマンドを使ってConfigMapリソースを作成します。
$ kubectl apply -f cwagent-configmap.yaml configmap/cwagentconfig created
(4) DaemonSetとしてCloudWatchエージェントをデプロイする
「DaemonSet」とは、Kubernetes上でコンテナを実行するワークロードの種類のうちの一つです。
DaemonSetで定義されたコンテナは、各ワーカーノード (EC2インスタンス) 上で常駐型のコンテナとして実行されます。
全てのワーカーノードにおいて、ワーカーノード1台に対してコンテナ1個が配置されます。
Container Insightsでは、各ワーカーノードでCloudWatchエージェントのコンテナが常駐することで、ワーカーノードのメトリクスおよびワーカーノード上で実行されるコンテナのメトリクスを収集します。
DaemonSstを作成するマニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cwagent/cwagent-daemonset.yaml
マニフェストファイルの内容 (クリックすると展開します)
# deploy cwagent as daemonset apiVersion: apps/v1 kind: DaemonSet metadata: name: cloudwatch-agent namespace: amazon-cloudwatch spec: selector: matchLabels: name: cloudwatch-agent template: metadata: labels: name: cloudwatch-agent spec: containers: - name: cloudwatch-agent image: amazon/cloudwatch-agent:1.231221.0 #ports: # - containerPort: 8125 # hostPort: 8125 # protocol: UDP resources: limits: cpu: 200m memory: 200Mi requests: cpu: 200m memory: 200Mi # Please don't change below envs env: - name: HOST_IP valueFrom: fieldRef: fieldPath: status.hostIP - name: HOST_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: K8S_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: CI_VERSION value: "k8s/1.1.0" # Please don't change the mountPath volumeMounts: - name: cwagentconfig mountPath: /etc/cwagentconfig - name: rootfs mountPath: /rootfs readOnly: true - name: dockersock mountPath: /var/run/docker.sock readOnly: true - name: varlibdocker mountPath: /var/lib/docker readOnly: true - name: sys mountPath: /sys readOnly: true - name: devdisk mountPath: /dev/disk readOnly: true volumes: - name: cwagentconfig configMap: name: cwagentconfig - name: rootfs hostPath: path: / - name: dockersock hostPath: path: /var/run/docker.sock - name: varlibdocker hostPath: path: /var/lib/docker - name: sys hostPath: path: /sys - name: devdisk hostPath: path: /dev/disk/ terminationGracePeriodSeconds: 60 serviceAccountName: cloudwatch-agent
DaemonSetで定義されるコンテナは、DockerHubのパブリックリポジトリでAWSが公開している amazon/cloudwatch-agent:1.231221.0
というコンテナイメージから起動されます。
マニフェストには env
や volumeMounts
などのパラメータが記述されていますが、変更せずにそのままとします。
kubernetes apply
コマンドを使ってDaemonSetリソースを作成します。
$ kubectl apply -f cwagent-daemonset.yaml daemonset.apps/cloudwatch-agent created
正常に作成されたことを確認するために、DaemonSetとPodの一覧を表示します。
$ kubectl get daemonsets -n amazon-cloudwatch NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE cloudwatch-agent 2 2 2 2 2 <none> 38s $ kubectl get pods -n amazon-cloudwatch -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES cloudwatch-agent-4427l 1/1 Running 0 96s 192.168.39.252 ip-192-168-36-52.ap-northeast-1.compute.internal <none> <none> cloudwatch-agent-7p2b2 1/1 Running 0 95s 192.168.25.224 ip-192-168-28-45.ap-northeast-1.compute.internal <none> <none>
各ワーカーノード上でPodが1つずつ実行されていることが確認できます。
もし、Podが起動しない場合や、起動・停止を繰り返す場合には、Podのイベントやログを確認して原因を調べます。
$ kubectl describe pod cloudwatch-agent-XXXXX -n amazon-cloudwatch $ kubectl logs cloudwatch-agent-XXXXX -n amazon-cloudwatch
例えば、正常に起動した場合のイベントは以下のようになっています。
$ kubectl describe pod cloudwatch-agent-4427l -n amazon-cloudwatch Name: cloudwatch-agent-4427l Namespace: amazon-cloudwatch (中略) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 2m56s default-scheduler Successfully assigned amazon-cloudwatch/cloudwatch-agent-4427l to ip-192-168-36-52.ap-northeast-1.compute.internal Normal Pulling 2m56s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Pulling image "amazon/cloudwatch-agent:1.231221.0" Normal Pulled 2m51s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Successfully pulled image "amazon/cloudwatch-agent:1.231221.0" Normal Created 2m51s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Created container cloudwatch-agent Normal Started 2m51s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Started container cloudwatch-agent
(5) CloudWatchへメトリクスが出力されることを確認する
DaemonSetをデプロイしてしばらく経つと、CloudWatchエージェントからCloudWatchへメトリクスが出力されるようになります。
マネジメントコンソールでCloudWatchのメトリクスを開いて、「カスタム名前空間」に「ContainerInsights」が表示されていることを確認します。
「ContainerInsights」名前空間の配下には、クラスター、ノード、ポッドなど、Kubernetesの各リソースに関するメトリクスが出力されていることが確認できます。
また、CloudWatchエージェントはメトリクスの他にログも出力します。
ロググループ /aws/containerinsights/(クラスター名)/performance
が作成されていることを確認します。
これらのメトリクス、ログが出力されていれば、CloudWatchエージェントは正常に動作しています。
もし出力されていない場合には、ここまでの手順を見直してください。
(特に、ワーカーノードのIAMロールにポリシーが追加されているか? Podのイベントやログにエラーが無いか? という点を確認すると良いと思います)
ステップ2: ログ収集を行うFluentdを導入する
Container Insightsのログ収集は、オープンソースのログ収集ソフトウェアである「Fluentd」が使われています。
「Fluentd」は様々な形態で提供されていますが、Container InsightsではKubernetes向けに提供されているFluentdコンテナイメージを用います。
導入は以下のページの内容に沿って進めます。
CloudWatch Logs へログを送信する DaemonSet として FluentD をセットアップする - Amazon CloudWatch
(1) 名前空間を作成する
FluentdはCloudWatchエージェント用のNamespace「amazon-cloudwatch」に配置する必要がありますが、ステップ1の手順で既に作成しているため、改めて作成する必要はありません。
(2) ConfigMapを作成する
Fluentdに対して設定を与えるConfigMapを作成します。
マニフェストのテンプレートファイルを以下のように作成します。
# create configmap for cluster name and aws region for CloudWatch Logs # need to replace the placeholders {{cluster_name}} and {{region_name}} apiVersion: v1 data: cluster.name: {{cluster_name}} logs.region: {{region_name}} kind: ConfigMap metadata: name: cluster-info namespace: amazon-cloudwatch
5行目の cluster.name: {{cluster_name}}
と、6行目の logs.region: {{region_name}}
の部分を修正します。
今回の例であれば以下のようにします。
`cluster.name: eks-example` `logs.region: ap-northeast-1`
kubernetes apply
コマンドを使ってConfigMapリソースを作成します。
$ kubectl apply -f fluentd-configmap.yaml configmap/cluster-info created
(3) Fluentdをインストールする
ここからは、Fluentdを構成する各種リソースを一気に作成します。
マニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/fluentd/fluentd.yaml
マニフェストファイルの内容 (クリックすると展開します)
apiVersion: v1 kind: ServiceAccount metadata: name: fluentd namespace: amazon-cloudwatch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: fluentd-role rules: - apiGroups: [""] resources: - namespaces - pods - pods/logs verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: fluentd-role-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: fluentd-role subjects: - kind: ServiceAccount name: fluentd namespace: amazon-cloudwatch --- apiVersion: v1 kind: ConfigMap metadata: name: fluentd-config namespace: amazon-cloudwatch labels: k8s-app: fluentd-cloudwatch data: fluent.conf: | @include containers.conf @include systemd.conf @include host.conf <match fluent.**> @type null </match> containers.conf: | <source> @type tail @id in_tail_container_logs @label @containers path /var/log/containers/*.log exclude_path ["/var/log/containers/cloudwatch-agent*", "/var/log/containers/fluentd*"] pos_file /var/log/fluentd-containers.log.pos tag * read_from_head true <parse> @type json time_format %Y-%m-%dT%H:%M:%S.%NZ </parse> </source> <source> @type tail @id in_tail_cwagent_logs @label @cwagentlogs path /var/log/containers/cloudwatch-agent* pos_file /var/log/cloudwatch-agent.log.pos tag * read_from_head true <parse> @type json time_format %Y-%m-%dT%H:%M:%S.%NZ </parse> </source> <source> @type tail @id in_tail_fluentd_logs @label @fluentdlogs path /var/log/containers/fluentd* pos_file /var/log/fluentd.log.pos tag * read_from_head true <parse> @type json time_format %Y-%m-%dT%H:%M:%S.%NZ </parse> </source> <label @fluentdlogs> <filter **> @type kubernetes_metadata @id filter_kube_metadata_fluentd </filter> <filter **> @type record_transformer @id filter_fluentd_stream_transformer <record> stream_name ${tag_parts[3]} </record> </filter> <match **> @type relabel @label @NORMAL </match> </label> <label @containers> <filter **> @type kubernetes_metadata @id filter_kube_metadata </filter> <filter **> @type record_transformer @id filter_containers_stream_transformer <record> stream_name ${tag_parts[3]} </record> </filter> <filter **> @type concat key log multiline_start_regexp /^\S/ separator "" flush_interval 5 timeout_label @NORMAL </filter> <match **> @type relabel @label @NORMAL </match> </label> <label @cwagentlogs> <filter **> @type kubernetes_metadata @id filter_kube_metadata_cwagent </filter> <filter **> @type record_transformer @id filter_cwagent_stream_transformer <record> stream_name ${tag_parts[3]} </record> </filter> <filter **> @type concat key log multiline_start_regexp /^\d{4}[-/]\d{1,2}[-/]\d{1,2}/ separator "" flush_interval 5 timeout_label @NORMAL </filter> <match **> @type relabel @label @NORMAL </match> </label> <label @NORMAL> <match **> @type cloudwatch_logs @id out_cloudwatch_logs_containers region "#{ENV.fetch('REGION')}" log_group_name "/aws/containerinsights/#{ENV.fetch('CLUSTER_NAME')}/application" log_stream_name_key stream_name remove_log_stream_name_key true auto_create_stream true <buffer> flush_interval 5 chunk_limit_size 2m queued_chunks_limit_size 32 retry_forever true </buffer> </match> </label> systemd.conf: | <source> @type systemd @id in_systemd_kubelet @label @systemd filters [{ "_SYSTEMD_UNIT": "kubelet.service" }] <entry> field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"} field_map_strict true </entry> path /var/log/journal <storage> @type local persistent true path /var/log/fluentd-journald-kubelet-pos.json </storage> read_from_head true tag kubelet.service </source> <source> @type systemd @id in_systemd_kubeproxy @label @systemd filters [{ "_SYSTEMD_UNIT": "kubeproxy.service" }] <entry> field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"} field_map_strict true </entry> path /var/log/journal <storage> @type local persistent true path /var/log/fluentd-journald-kubeproxy-pos.json </storage> read_from_head true tag kubeproxy.service </source> <source> @type systemd @id in_systemd_docker @label @systemd filters [{ "_SYSTEMD_UNIT": "docker.service" }] <entry> field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"} field_map_strict true </entry> path /var/log/journal <storage> @type local persistent true path /var/log/fluentd-journald-docker-pos.json </storage> read_from_head true tag docker.service </source> <label @systemd> <filter **> @type kubernetes_metadata @id filter_kube_metadata_systemd </filter> <filter **> @type record_transformer @id filter_systemd_stream_transformer <record> stream_name ${tag}-${record["hostname"]} </record> </filter> <match **> @type cloudwatch_logs @id out_cloudwatch_logs_systemd region "#{ENV.fetch('REGION')}" log_group_name "/aws/containerinsights/#{ENV.fetch('CLUSTER_NAME')}/dataplane" log_stream_name_key stream_name auto_create_stream true remove_log_stream_name_key true <buffer> flush_interval 5 chunk_limit_size 2m queued_chunks_limit_size 32 retry_forever true </buffer> </match> </label> host.conf: | <source> @type tail @id in_tail_dmesg @label @hostlogs path /var/log/dmesg pos_file /var/log/dmesg.log.pos tag host.dmesg read_from_head true <parse> @type syslog </parse> </source> <source> @type tail @id in_tail_secure @label @hostlogs path /var/log/secure pos_file /var/log/secure.log.pos tag host.secure read_from_head true <parse> @type syslog </parse> </source> <source> @type tail @id in_tail_messages @label @hostlogs path /var/log/messages pos_file /var/log/messages.log.pos tag host.messages read_from_head true <parse> @type syslog </parse> </source> <label @hostlogs> <filter **> @type kubernetes_metadata @id filter_kube_metadata_host </filter> <filter **> @type record_transformer @id filter_containers_stream_transformer_host <record> stream_name ${tag}-${record["host"]} </record> </filter> <match host.**> @type cloudwatch_logs @id out_cloudwatch_logs_host_logs region "#{ENV.fetch('REGION')}" log_group_name "/aws/containerinsights/#{ENV.fetch('CLUSTER_NAME')}/host" log_stream_name_key stream_name remove_log_stream_name_key true auto_create_stream true <buffer> flush_interval 5 chunk_limit_size 2m queued_chunks_limit_size 32 retry_forever true </buffer> </match> </label> --- apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-cloudwatch namespace: amazon-cloudwatch spec: selector: matchLabels: k8s-app: fluentd-cloudwatch template: metadata: labels: k8s-app: fluentd-cloudwatch annotations: configHash: 8915de4cf9c3551a8dc74c0137a3e83569d28c71044b0359c2578d2e0461825 spec: serviceAccountName: fluentd terminationGracePeriodSeconds: 30 # Because the image's entrypoint requires to write on /fluentd/etc but we mount configmap there which is read-only, # this initContainers workaround or other is needed. # See https://github.com/fluent/fluentd-kubernetes-daemonset/issues/90 initContainers: - name: copy-fluentd-config image: busybox command: ['sh', '-c', 'cp /config-volume/..data/* /fluentd/etc'] volumeMounts: - name: config-volume mountPath: /config-volume - name: fluentdconf mountPath: /fluentd/etc - name: update-log-driver image: busybox command: ['sh','-c',''] containers: - name: fluentd-cloudwatch image: fluent/fluentd-kubernetes-daemonset:v1.7.3-debian-cloudwatch-1.0 env: - name: REGION valueFrom: configMapKeyRef: name: cluster-info key: logs.region - name: CLUSTER_NAME valueFrom: configMapKeyRef: name: cluster-info key: cluster.name - name: CI_VERSION value: "k8s/1.1.0" resources: limits: memory: 400Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: config-volume mountPath: /config-volume - name: fluentdconf mountPath: /fluentd/etc - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: runlogjournal mountPath: /run/log/journal readOnly: true - name: dmesg mountPath: /var/log/dmesg readOnly: true volumes: - name: config-volume configMap: name: fluentd-config - name: fluentdconf emptyDir: {} - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: runlogjournal hostPath: path: /run/log/journal - name: dmesg hostPath: path: /var/log/dmesg
このマニフェストには以下のリソースが含まれています。
- ServiceAccount「fluentd」
- ClusterRole「fluentd-role」
- ClusterRoleBinding「fluentd-role-binding」
- ConfigMap「fluentd-config」(*)
- DaemonSet「fluentd-cloudwatch」
(*) このConfigMapは、前の手順で作成したConfigMapとは別物です。Fluentdの動作をカスタマイズする様々な設定が含まれますが、今回はデフォルト設定のまま進めます。
これらのリソースの役割は、CloudWatchエージェントの時とほぼ同様です。
ServiceAccount、ClusterRole、ClusterRoleBindingによってFluentdの動作に必要な権限を設定して、DaemonSetによってFluentdを常駐型コンテナとして実行します。
kubernetes apply
コマンドを使って各リソースを作成します。
$ kubectl apply -f fluentd.yaml serviceaccount/fluentd created clusterrole.rbac.authorization.k8s.io/fluentd-role created clusterrolebinding.rbac.authorization.k8s.io/fluentd-role-binding created configmap/fluentd-config created daemonset.apps/fluentd-cloudwatch created
正常に作成されたことを確認するために、DaemonSetとPodの一覧を表示します。
$ kubectl get daemonsets -n amazon-cloudwatch NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE cloudwatch-agent 2 2 2 2 2 <none> 71m fluentd-cloudwatch 2 2 2 2 2 <none> 85s $ kubectl get pods -n amazon-cloudwatch -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES cloudwatch-agent-4427l 1/1 Running 0 71m 192.168.39.252 ip-192-168-36-52.ap-northeast-1.compute.internal <none> <none> cloudwatch-agent-7p2b2 1/1 Running 0 71m 192.168.25.224 ip-192-168-28-45.ap-northeast-1.compute.internal <none> <none> fluentd-cloudwatch-g9jm8 1/1 Running 0 105s 192.168.52.175 ip-192-168-36-52.ap-northeast-1.compute.internal <none> <none> fluentd-cloudwatch-gdv4b 1/1 Running 0 105s 192.168.8.71 ip-192-168-28-45.ap-northeast-1.compute.internal <none> <none>
ステップ1で作成したCloudWatchエージェントのDaemonSetやPodに加えて、FluentdのDaemonSetとPodが作成されていることが確認できます。
(4) CloudWatchへログが出力されることを確認する
Fluentdをデプロイしてしばらく経つと、CloudWatchエージェントからCloudWatchへメトリクスが出力されるようになります。
マネジメントコンソールでCloudWatchのログを開いて、以下の3つのロググループが作成されていることを確認します。
/aws/containerinsights/(クラスター名)/application
/aws/containerinsights/(クラスター名)/host
/aws/containerinsights/(クラスター名)/dataplane
これらのログが出力されていれば、Fluentdは正常に動作しています。
Container Insightsのダッシュボードを表示する
CloudWatchのトップ画面を表示します。
「概要」プルダウンをクリックして「Container Insights」を選択します。
Container Insightsのダッシュボードが表示され、EKSクラスターのメトリクスデータがグラフ表示されていることを確認します。
おわりに
今回ご紹介した手順を一通り試してみることにより、Container InsightsがどのようなKubernetesのリソースによって動作しているのかを理解することができるのではないかと思います。
Container Insightsを素早く導入するには「クイックスタート」の手順に沿って行うのが良いでしょう。
クイックスタートのマニフェストを使う際に、今回の内容を参考にしてマニフェストをカスタマイズすることも可能かと思います。